<?php

namespace common\base;

use Yii;
use yii\base\Exception;
use yii\helpers\Inflector;
use yii\db\Transaction;

/**
 * Model is the base class for data models.
 * 
 * @author emhome <emhome@163.com>
 * @since 1.0
 */
class Model extends \yii\base\Model {

    use \common\traits\Constants;
    use \common\traits\ModelErrors;
    use \common\traits\DataPierces;

    const TRANS_FUNC_PREFIX = 'transact_';

    /**
     * @event ModelEvent an event raised at the beginning of [[validate()]]. You may set
     * [[ModelEvent::isValid]] to be false to stop the validation.
     */
    const EVENT_BEFORE_TRANSACT = 'beforeTransact';

    /**
     * @event Event an event raised at the end of [[validate()]]
     */
    const EVENT_AFTER_TRANSACT = 'afterTransact';

    /**
     * @param string $method
     */
    public function beforeTransact($method) {
        $event = new TransactModelEvent();
        $this->trigger(self::EVENT_BEFORE_TRANSACT, $event);

        return $event->isValid;
    }

    /**
     * 
     * @param string $method
     * @param mixed $result
     */
    public function afterTransact($method, $result) {
        $this->trigger(self::EVENT_AFTER_TRANSACT);
    }

    /**
     * AR事务封装
     *
     * @param $name
     * @param null $error
     * @return mixed|void
     * @throws ModelHttpException
     */
    public function executeTransaction($name, $error = null, $xadbs = []) {
        $method = Inflector::variablize(self::TRANS_FUNC_PREFIX . $name);
        $handler = array($this, $method);
        if (!$this->hasMethod($method) || !is_callable($handler)) {
            Yii::error([
                $handler,
                $this->hasMethod($method),
                is_callable($handler),
            ]);
            return $this->dumpError('内部操作失败，不存在的操作类型！');
        }
        if (!$this->beforeTransact($method)) {
            return $this->dumpError();
        }

        //执行事务操作
        $transactions = [
            Yii::$app->db->beginTransaction()
        ];
        foreach ($xadbs as $dbname) {
            $_connection = Yii::$app->get($dbname);
            if ($_connection) {
                $transactions[] = $_connection->beginTransaction();
            }
        }
        try {
            $executeResult = call_user_func($handler);
            foreach ($transactions as $transaction) {
                $transaction->commit();
            }
        } catch (\Exception $e) {
            foreach ($transactions as $transaction) {
                $transaction->rollBack();
            }
            if ($error !== null) {
                return $error;
            }
            $msg = $e->getMessage();
            Yii::error([
                $msg,
            ], __METHOD__);
            return $this->saveError($msg, false);
        } catch (\Throwable $t) {
            foreach ($transactions as $transaction) {
                $transaction->rollBack();
            }
            if ($error !== null) {
                return $error;
            }
            $msg = $t->getMessage();
            Yii::error([
                $msg,
            ], __METHOD__);
            return $this->saveError($msg, false);
        }
        $this->afterTransact($method, $executeResult);
        return $executeResult;
    }

    /**
     * Populates the model with input data.
     * @return bool whether `load()` found the expected form in `$data`.
     */
    public function load($data, $formName = '') {
        return parent::load($data, $formName);
    }

}
